You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Framework release scope follows the v4.2.0 section of CHANGELOG.md, with Product Search and MCP demonstrations showing the capabilities in a distributed sample.
Framework enhancements
Presence-aware request values:Optional<T> distinguishes omitted fields from supplied null/default values on explicit request/patch DTOs. Includes JSON conversion/omission, contract cleaning, generated reference-data support and inner-type OpenAPI schemas (including collection/dictionary shapes). Validate supplied values with .Unwrap() and require presence with .Specified(); map only specified fields into ordinary application/domain models. Custom wrappers can opt into IWrappedValueCleaner<TSelf> with cached constrained dispatch avoiding receiver/result boxing on typed cleaning paths. Object/interface boundaries and inner cleaning can still allocate.
Expression-based field errors:MessageItem.CreateErrorMessage<TEntity>, ValidationException.Create<TEntity>, Result.ValidationError<TEntity> and Result<T>.ValidationError<TEntity> resolve friendly/localized labels and JSON paths without executing entity access. Support nested properties, array/list indexes and string dictionary keys; omitted messages infer the standard invalid message. RuntimeMetadata.GetJsonPath / GetJsonPathAndText provide shared resolution, with explicit serializer naming options supported.
HTTP request builders: single-use Send(method, uri) and Get/Post/Put/Patch/Delete shortcuts, WithJson for JSON and WithContent for explicit content, also available on HttpRequestMessage. Conflicting body assignments, repeated/concurrent execution and configuration after execution starts throw InvalidOperationException. PATCH does not imply JSON Merge Patch.
WithStatusResult / WithErrorStatus, including validated builders, intercept selected statuses before deserialization/validation. ValidatedHttpRequestBuilder<T> integrates response validation and automatic response disposal.
HttpResponseMessage.GetStringAsync() / ToStringResultAsync() and corresponding builder terminals provide raw text, returning null for successful empty/204 responses.
Host-builder AddNamedHttpClient() / AddHttpClient() share idempotency, service discovery and resilience with typed clients. Separate configuration-section support augments SDK-owned named clients; existing registrations or callbacks can supply base addresses.
IEfDb.SqlQuery<T>(SqlStatement, params object[]) provides deferred, parameterized, composable scalar/unmapped EF queries using the existing context and transaction. CoreEx tenant/logical-delete filters are not automatic.
ReferenceDataOrchestrator.PrefetchAsync(IEnumerable<Type>) validates all types before loading, deduplicates requests and honors configured parallelism. Name-based prefetch checks cancellation immediately.
Validation DependsOn predicate overload runs its attached rule only when the dependency is non-default, has no validation error and satisfies the additional predicate.
Free-text $search across QueryArgs, configurable keyword/full-text/semantic/hybrid parsing, query schema, HTTP helpers, MVC/Minimal API metadata, NSwag and GraphQL-lite list roots. Keyword predicates execute through QueryExtensions.Where; repositories own ranked retrieval. Explicit $orderby is rejected for ranked search, and unconfigured search is rejected.
DataResult<T>.Map transforms mutation values while preserving WasMutated, including successful no-ops.
Shipped AI guidance and reference-data documentation explain implicit code-to-concrete-type conversion, annotated contract code properties and the separation of conversion from validation.
Behavior changes
String-specific Required() rejects empty/whitespace strings as well as null with ValidationException.
JsonDefaults.DictionaryKeyPolicy is now null, preserving dictionary keys and avoiding identity changes/casing collisions. Consumers requiring previous transformed keys must configure that policy explicitly; property naming is unchanged.
JSON Merge Patch matches names case-sensitively (StringComparer.Ordinal) under RFC 7396. Explicitly configure PropertyNameComparer = StringComparer.OrdinalIgnoreCase only for previous non-standard compatibility.
Shopping repository mapping preserves aggregate mutation metadata; Create/Update/Checkout events use WhereMutated, with successful-no-op publication regressions.
Domain guidance and Shopping pricing/discount examples distinguish coded business errors for aggregate/entity/value-object invariants from request field-validation errors. Request validation and domain enforcement remain separate; shared argument/reference-data guards retain their semantics.
Fixes and maintenance
Common validators preserve nullable null state instead of treating null as zero, retain caller labels/value formatting and propagate normalization through subsequent rules and AdditionalAsync callbacks. Unwrapped validation uses inner values in messages and nested entity paths and writes normalization back to writable Optional<T> properties.
String, comparison, entity and common validation extension receivers accept nullable reference inputs without nullable-annotation warnings while preserving fluent return types and runtime null behavior.
Exact-type internal casts take their fast path before null/pattern checks, avoiding unnecessary value-type boxing in typed metadata reads. Common validation tracks null state separately from typed values.
HTTP registration binds resilience settings from the nested Resilience section rather than the parent client section.
JSON Merge Patch recursively processes object patches for new members/non-object targets, removing null-valued patch members rather than copying them literally; removing a nonexistent member no longer reports a change.
Dictionary validation paths honor the effective serializer's DictionaryKeyPolicy for key/value errors, matching serialization and expression factories. CLR paths and GetDictionaryKey() retain original keys.
Cached property metadata consistently prefers static IRuntimeMetadata providers, including explicit implementations, with reflection fallback. Nested JSON paths/friendly labels respect custom/generated metadata regardless of cache order; explicit lookups separately cache reflected metadata for omitted properties without changing contract equality/copy/clean collections.
GraphQL-lite search parsing errors preserve actionable details in extensions.messages["$search"] while retaining SEARCH_PARSE_ERROR.
WithQuery(QueryArgs) forwards IsIncludeText and IsIncludeInactive.
Framework/template dependency updates include Aspire 13.6.1, EF/.NET servicing and FusionCache 2.9.0; PKCS 10.0.13 and MessagePack 2.5.307 overrides remain. Aspire 13.6.2 is not included.
Explicit MessagePack 2.5.307 references in CoreEx.UnitTesting and sample/template MockHost prevent vulnerable transitive resolution; no audit-warning suppression was added.
Stable SDK pinned in global.json for local development, CI and publishing. nuget-publish.ps1 builds before packing and removes matching package versions from the local NuGet cache by default.
Application/API instructions, policy/service/adapter skills and package/sample guidance distinguish missing targets (404) from invalid request-body references (field-level 400), including query filters and expected downstream lookup outcomes.
SQL guidance favors parameterized embedded resources for application-owned static SQL; FromText is reserved for runtime-generated SQL. Optional guidance covers request-boundary mapping, wrapper extensibility, normalization and four-type nullable/non-nullable regression coverage. Markdown paragraphs remain unwrapped and XML documentation conventions cover private members.
Product Search demonstration
Products-owned search exposed through REST and GraphQL-lite, with descriptions, structured filters and price constraints.
Exact pgvector cosine-distance retrieval using pinned local Ollama/Nomic embeddings behind an Application-owned adapter.
Independent PostgreSQL full-text retrieval and opt-in Hybrid RRF (k=60, up to 100 candidates per branch), eligible exact-SKU priority and deterministic ordering/paging.
Explicit rebuild/readiness and asynchronous outbox/relay/Service Bus/subscriber reconciliation across product creation, updates, activation and logical deletion, including stale-write/inference-race handling.
Fixed hybrid-search final candidate lookup on .NET 8 with list-based membership rather than array Contains, avoiding unsupported span expressions during EF query evaluation.
Shared mountain-bike catalog, interactive runner search/setup, provenance and curated relevance evaluation. Historical v1/pre-SKU-rename v2 observations are not current-catalog quality gates; HNSW benchmarking remains future work.
Updated setup, architecture, lifecycle, evaluation and operational documentation, scaffold command/skill descriptions and AppHost cleanup.
MCP demonstration
Stateless Streamable HTTP Contoso.Mcp facade over real Products/Shopping APIs, wired through Aspire service discovery.
search_products forwards search text, category/subcategory/brand and inclusive price filters; get_product_search_options discovers live codes. MCP owns no database querying, embeddings or indexes.
Structured product and basket lifecycle tools: list/create/read baskets, add/update/remove items, discounts, shipping address and checkout through the existing inventory/outbox flow.
Direct tool coverage for forwarding, filtering, encoding, validation, errors and schema; SDK-driven HTTP E2E for discovery/basket lifecycle and separate real asynchronous product indexing coverage.
samples/docs/search.md includes MCP Inspector and Copilot CLI checks for discovery, structured filters and evidence-grounded responses. Deterministic tool/protocol tests are distinct from exploratory, model-dependent tool-selection evaluation.
The unauthenticated MCP host is local-development only; manual search checks are read-only and automated E2E setup requires disposable infrastructure.
Verification
All reported PR checks passed for final head commit 249df992962013aa99feb4f49c2d519606d624b9.
CodeQL analysis: Actions and C# analysis completed successfully; the separate CodeQL check also passed.
DCO check passed.
Copilot Code Review workflow completed successfully; workflow success does not itself imply that every review comment is resolved.
Final local verification for the same commit (.NET 10 Debug, warnings treated as errors):
Suite
Passed
CoreEx
1,278
CoreEx.Validation
194
CoreEx.AspNetCore
384
CoreEx.RefData
152
Total
2,008
Allocation regressions measure zero bytes across 10,000 warmed-up typed metadata reads and typed wrapper-cleaning calls for the covered cases; this is not an allocation-free guarantee for the entire pipeline.
Earlier targeted verification also included 183 GraphQL tests on .NET 10, 173 Products API tests on each of .NET 8/9/10 (519 total), 143 HTTP tests, 21 MCP ProductSearchToolTests, a full solution restore with CI=true, a multi-target CoreEx.UnitTesting build and 26/26 template validation scenarios. The successful CI run linked above provides the final automated result for this PR head.
Wire configurable keyword and ranked search through querying, HTTP, OpenAPI and GraphQL. Update the Products and MCP samples, tests and querying documentation.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Signed-off-by: Eric Sibly <eric.sibly@avanade.com>
Adopt DbEx 3.3.1 and patched dependency versions across framework and templates. Add opt-in net10 local builds, build-output cleanup, and leaner template validation. Preserve shared feed packages and publish only packages generated by the current run.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Signed-off-by: Eric Sibly <eric.sibly@avanade.com>
Capture reviewed catalog judgments and a real-model baseline, isolate evaluation data setup, and add manual/monthly reporting with retained artifacts.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Signed-off-by: Eric Sibly <eric.sibly@avanade.com>
Introduce independently maintained PostgreSQL full-text retrieval with exact-SKU priority and tuning-only relevance evaluation. Separate product query orchestration from CRUD and centralize server search-mode resolution.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Signed-off-by: Eric Sibly <eric.sibly@avanade.com>
Combine bounded lexical and semantic retrieval with reciprocal rank fusion. Expand the reviewed demo catalog and evaluation set, share catalog and reference data across demo setup, bootstrap Shopping through its sync adapter, and target the E2E Runner at net10. Normalize internal catalog SKUs without artificial DEMO prefixes.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Signed-off-by: Eric Sibly <eric.sibly@avanade.com>
Add search-session schema caching and catalog bootstrap progress, enable Hybrid in Aspire, and resolve local target frameworks early for Visual Studio.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Signed-off-by: Eric Sibly <eric.sibly@avanade.com>
Expose structured category, subcategory, brand and price filters on the MCP product search tool, add a live filter-options discovery tool, and cover the new contract with focused Aspire tests.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Signed-off-by: Eric Sibly <eric.sibly@avanade.com>
Pin patched MessagePack in CoreEx.UnitTesting and the sample/template MockHost so transitive consumers resolve 2.5.307. Include remaining scaffold descriptions and AppHost cleanup.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Signed-off-by: Eric Sibly <eric.sibly@avanade.com>
Use list-based candidate membership to avoid span expressions during EF query evaluation, and document the compatibility constraint.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Signed-off-by: Eric Sibly <eric.sibly@avanade.com>
The reason will be displayed to describe this comment to others. Learn more.
🔵 Needs a closer look
GraphQL search errors discard actionable validation details; preserve them before approval.
No open issues 🎉
1 previously missed issue
In code that hasn't changed since last review
Surface query search parsing details in GraphQL errors
src/CoreEx.Data.GraphQL/GraphQLEngine.cs:367
QuerySearchParserException.Message is always the fallback "A query search parsing error occurred."; the actual reason (unsupported search, maximum length, or a custom validation failure) is stored in Messages. This branch bypasses MapValidationException and discards those details, leaving GraphQL callers unable to determine how to correct their search. Surface the stored message while retaining SEARCH_PARSE_ERROR.
Fixed the GraphQL search validation-detail finding from #189 (review) in commit e9e32f2. QuerySearchParserException now uses the shared validation-error mapper, preserving actionable details in extensions.messages["$search"] while retaining SEARCH_PARSE_ERROR and the root alias path. Added regression coverage for unsupported search, maximum length, term limits and custom validation; all four cases failed before the fix, and all 183 GraphQL unit tests passed afterward. README and v4.2.0 changelog are updated.
The latest automated review reports no open issues. The original finding was published in a review summary rather than an inline review thread, so GitHub provides no thread to mark resolved; this comment records its resolution.
Add expression-based validation factories and cached metadata fallbacks, distinguish request validation from domain business errors, and preserve DataResult mutation flags through mapping and event publication. Align samples, regression coverage, and AI guidance.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Signed-off-by: Eric Sibly <eric.sibly@avanade.com>
Use case-sensitive RFC 7396 member matching by default, recursively merge object patches, and avoid changes for absent-member removals. Preserve dictionary keys in serializer defaults and align regression coverage and documentation.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Signed-off-by: Eric Sibly <eric.sibly@avanade.com>
Update expected SQL Server, PostgreSQL and Redis health entry names to match their registered casing across sample and generated host tests.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Signed-off-by: Eric Sibly <eric.sibly@avanade.com>
Support presence-aware JSON, validation, reference-data generation and OpenAPI. Add extensible typed wrapper cleaning, preserve nullable validation state and normalization, and update guidance and release notes.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Signed-off-by: Eric Sibly <eric.sibly@avanade.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
CoreEx v4.2.0
Framework release scope follows the v4.2.0 section of
CHANGELOG.md, with Product Search and MCP demonstrations showing the capabilities in a distributed sample.Framework enhancements
Optional<T>distinguishes omitted fields from supplied null/default values on explicit request/patch DTOs. Includes JSON conversion/omission, contract cleaning, generated reference-data support and inner-type OpenAPI schemas (including collection/dictionary shapes). Validate supplied values with.Unwrap()and require presence with.Specified(); map only specified fields into ordinary application/domain models. Custom wrappers can opt intoIWrappedValueCleaner<TSelf>with cached constrained dispatch avoiding receiver/result boxing on typed cleaning paths. Object/interface boundaries and inner cleaning can still allocate.MessageItem.CreateErrorMessage<TEntity>,ValidationException.Create<TEntity>,Result.ValidationError<TEntity>andResult<T>.ValidationError<TEntity>resolve friendly/localized labels and JSON paths without executing entity access. Support nested properties, array/list indexes and string dictionary keys; omitted messages infer the standard invalid message.RuntimeMetadata.GetJsonPath/GetJsonPathAndTextprovide shared resolution, with explicit serializer naming options supported.Send(method, uri)andGet/Post/Put/Patch/Deleteshortcuts,WithJsonfor JSON andWithContentfor explicit content, also available onHttpRequestMessage. Conflicting body assignments, repeated/concurrent execution and configuration after execution starts throwInvalidOperationException. PATCH does not imply JSON Merge Patch.WithStatusResult/WithErrorStatus, including validated builders, intercept selected statuses before deserialization/validation.ValidatedHttpRequestBuilder<T>integrates response validation and automatic response disposal.HttpResponseMessage.GetStringAsync()/ToStringResultAsync()and corresponding builder terminals provide raw text, returning null for successful empty/204 responses.AddNamedHttpClient()/AddHttpClient()share idempotency, service discovery and resilience with typed clients. Separate configuration-section support augments SDK-owned named clients; existing registrations or callbacks can supply base addresses.IEfDb.SqlQuery<T>(SqlStatement, params object[])provides deferred, parameterized, composable scalar/unmapped EF queries using the existing context and transaction. CoreEx tenant/logical-delete filters are not automatic.ReferenceDataOrchestrator.PrefetchAsync(IEnumerable<Type>)validates all types before loading, deduplicates requests and honors configured parallelism. Name-based prefetch checks cancellation immediately.DependsOnpredicate overload runs its attached rule only when the dependency is non-default, has no validation error and satisfies the additional predicate.$searchacrossQueryArgs, configurable keyword/full-text/semantic/hybrid parsing, query schema, HTTP helpers, MVC/Minimal API metadata, NSwag and GraphQL-lite list roots. Keyword predicates execute throughQueryExtensions.Where; repositories own ranked retrieval. Explicit$orderbyis rejected for ranked search, and unconfigured search is rejected.DataResult<T>.Maptransforms mutation values while preservingWasMutated, including successful no-ops.Behavior changes
Required()rejects empty/whitespace strings as well as null withValidationException.JsonDefaults.DictionaryKeyPolicyis now null, preserving dictionary keys and avoiding identity changes/casing collisions. Consumers requiring previous transformed keys must configure that policy explicitly; property naming is unchanged.StringComparer.Ordinal) under RFC 7396. Explicitly configurePropertyNameComparer = StringComparer.OrdinalIgnoreCaseonly for previous non-standard compatibility.WhereMutated, with successful-no-op publication regressions.Fixes and maintenance
AdditionalAsynccallbacks. Unwrapped validation uses inner values in messages and nested entity paths and writes normalization back to writableOptional<T>properties.Resiliencesection rather than the parent client section.DictionaryKeyPolicyfor key/value errors, matching serialization and expression factories. CLR paths andGetDictionaryKey()retain original keys.IRuntimeMetadataproviders, including explicit implementations, with reflection fallback. Nested JSON paths/friendly labels respect custom/generated metadata regardless of cache order; explicit lookups separately cache reflected metadata for omitted properties without changing contract equality/copy/clean collections.extensions.messages["$search"]while retainingSEARCH_PARSE_ERROR.DatabaseArgs.IsolationLevel; nested savepoints inherit parent isolation.WithQuery(QueryArgs)forwardsIsIncludeTextandIsIncludeInactive.CoreEx.UnitTestingand sample/template MockHost prevent vulnerable transitive resolution; no audit-warning suppression was added.global.jsonfor local development, CI and publishing.nuget-publish.ps1builds before packing and removes matching package versions from the local NuGet cache by default.FromTextis reserved for runtime-generated SQL. Optional guidance covers request-boundary mapping, wrapper extensibility, normalization and four-type nullable/non-nullable regression coverage. Markdown paragraphs remain unwrapped and XML documentation conventions cover private members.Product Search demonstration
k=60, up to 100 candidates per branch), eligible exact-SKU priority and deterministic ordering/paging.Contains, avoiding unsupported span expressions during EF query evaluation.MCP demonstration
Contoso.Mcpfacade over real Products/Shopping APIs, wired through Aspire service discovery.search_productsforwards search text, category/subcategory/brand and inclusive price filters;get_product_search_optionsdiscovers live codes. MCP owns no database querying, embeddings or indexes.samples/docs/search.mdincludes MCP Inspector and Copilot CLI checks for discovery, structured filters and evidence-grounded responses. Deterministic tool/protocol tests are distinct from exploratory, model-dependent tool-selection evaluation.Verification
All reported PR checks passed for final head commit
249df992962013aa99feb4f49c2d519606d624b9.Final local verification for the same commit (.NET 10 Debug, warnings treated as errors):
Allocation regressions measure zero bytes across 10,000 warmed-up typed metadata reads and typed wrapper-cleaning calls for the covered cases; this is not an allocation-free guarantee for the entire pipeline.
Earlier targeted verification also included 183 GraphQL tests on .NET 10, 173 Products API tests on each of .NET 8/9/10 (519 total), 143 HTTP tests, 21 MCP ProductSearchToolTests, a full solution restore with
CI=true, a multi-targetCoreEx.UnitTestingbuild and 26/26 template validation scenarios. The successful CI run linked above provides the final automated result for this PR head.